#ifndef __OS_FILE__
#define __OS_FILE__

namespace xm
{
	static inline HRESULT LastResult()
	{
		return HRESULT_FROM_WIN32(GetLastError());
	}

	static DWORD Access( SIZE_T OpenMode )
	{
		DWORD dwAcc = 0;
		if( OpenMode & F_WRITE )
			dwAcc |= GENERIC_WRITE;
		if( OpenMode & F_READ )
			dwAcc |= GENERIC_READ;
		return dwAcc;
	}
	static DWORD Creation( SIZE_T OpenMode )
	{
		DWORD dwMode = OpenMode &(F_OPEN|F_CREATE|F_TRUNC);
		switch( dwMode )
		{
		case F_OPEN: return OPEN_EXISTING; 
		case F_OPEN|F_CREATE: return OPEN_ALWAYS; 
		case F_OPEN|F_TRUNC: 
		case F_TRUNC: return TRUNCATE_EXISTING;
		case F_OPEN|F_CREATE|F_TRUNC: return CREATE_ALWAYS; 
		case F_CREATE: return CREATE_NEW;
		case F_CREATE|F_TRUNC: return CREATE_ALWAYS;
		};
		return 0;
	}
	static DWORD FFFA( SIZE_T OpenMode )
	{
		DWORD fa = 0;
		if( OpenMode & F_MAPPING )
			fa |= FILE_FLAG_NO_BUFFERING;
		if( OpenMode & F_AUTODEL )
			fa |= FILE_FLAG_DELETE_ON_CLOSE|FILE_ATTRIBUTE_TEMPORARY;
		return fa;
	}

	class WinNativeFile : public IKFileStream, public IKNativeFile, UnkBase
	{
	protected:
		HANDLE			_hFile;
		xv::xvalue_t	_FileName;
		OPENMODE		_OpenMode;
		FSTATUS			_Status;
	public:
		UNKNOWN_IMP3_( IKFileStream, IKNativeFile, IKBStream );
		HRESULT init_class( IUnknown * , IUnknown * )
		{
			return S_OK;
		}
	public:
		WinNativeFile( HANDLE hFile = NULL ) : _hFile(hFile), _OpenMode(0), _Status(0)
		{
		}
		~WinNativeFile()
		{
			Close();
		}
		STDMETHOD_(FSTATUS,GetStatus)() 
		{
			return _Status;
		}
		STDMETHOD(ModifyStatus)( FSTATUS uAdd, FSTATUS uRemove = 0 )
		{
			_Status &= ~uRemove;
			_Status |= uAdd;
			return S_OK;
		}
		STDMETHOD_(BOOL,IsVirtual)()
		{
			return FALSE;
		}
		STDMETHOD_(ACCESSMASK,GetAccess)()
		{
			return _OpenMode & F_ACCESS_MASK;
		}
		STDMETHOD(GetFileName)( xv::AnyValue& fileName )
		{
			if( !_IsValidHandle(_hFile) ) 
				return E_UNEXPECTED;
			_FileName.CopyTo( fileName );
			return S_OK;
		}
		STDMETHOD(Remove)()
		{
			_Status |= F_DELETED;
			return S_OK;
		}
		STDMETHOD(SetAccess)( ACCESSMASK uAccess )
		{
			if( !_IsValidHandle(_hFile) ) 
				return E_UNEXPECTED;
			OPENMODE uMode = (uAccess & F_ACCESS_MASK) | F_OPEN ;
			HANDLE hFile = 0;
			if( _FileName.isMBS() )
				hFile = CreateFileA( _FileName, Access(uMode), 7, 0, Creation(uMode), FFFA(uMode), 0 );
			else
				hFile = CreateFileW( _FileName, Access(uMode), 7, 0, Creation(uMode), FFFA(uMode), 0 );
			if( hFile == INVALID_HANDLE_VALUE ) 
				return LastResult();
			CloseHandle( _hFile );
			_OpenMode = uMode;
			return S_OK;
		}
		STDMETHOD(Open)( LPCSTR aFileNameA, OPENMODE uOpenMode )
		{
			Close();
			if( !aFileNameA ) return E_FAIL;
			HANDLE hFile = CreateFileA( aFileNameA, Access(uOpenMode), 7, 0, Creation(uOpenMode), FFFA(uOpenMode), 0 );
			if( hFile == INVALID_HANDLE_VALUE ) return LastResult();
			_hFile = hFile;
			_FileName = aFileNameA;
			_OpenMode = uOpenMode;
			return S_OK;
		}
		STDMETHOD(Open)( LPCWSTR aFileNameW, OPENMODE uOpenMode )
		{
			Close();
			if( !aFileNameW ) return E_FAIL;
			HANDLE hFile = CreateFileW( aFileNameW, Access(uOpenMode), 7, 0, Creation(uOpenMode), FFFA(uOpenMode), 0 );
			if( hFile == INVALID_HANDLE_VALUE ) return LastResult();
			_hFile = hFile;
			_FileName = aFileNameW;
			_OpenMode = uOpenMode;
			return S_OK;
		}
		STDMETHOD(GetHandle)( void** phHandle )
		{
			if( !phHandle ) return E_INVALIDARG;
			if( !_IsValidHandle(_hFile) ) 
				return E_UNEXPECTED;
			*phHandle = _hFile;
			return S_OK;
		}
		static BOOL _IsValidHandle( HANDLE h )
		{
			return (h != INVALID_HANDLE_VALUE) && (h != NULL);
		}
		STDMETHOD(Close)()
		{
			if( _IsValidHandle(_hFile) )
				::CloseHandle( _hFile );
			_hFile = NULL;
			_FileName.clear();
			_OpenMode = 0;
			_Status = 0;
			return S_OK;
		}
		STDMETHOD(Truncate)()
		{
			if( !_IsValidHandle(_hFile) ) return E_UNEXPECTED;
			BOOL fok = SetEndOfFile( _hFile );
			if( !fok ) return LastResult();
			_Status |= F_MODIFIED;
			return S_OK;
		}
		STDMETHOD(Read)( LPVOID lpBuffer, SIZE_T cbData, SIZE_T * lpEff )
		{
			DWORD cbRead = 0;
			BOOL fok = ::ReadFile( _hFile, lpBuffer, (DWORD)cbData, &cbRead, NULL );
			if( !fok ) return LastResult();
			if( lpEff ) *lpEff = cbRead;
			return S_OK;
		}
		STDMETHOD(Write)( LPCVOID lpBuffer, SIZE_T cbData, SIZE_T * lpEff )
		{
			if(!(_OpenMode&F_WRITE))
				return E_ACCESSDENIED;
			DWORD cbWritten = 0;
			BOOL fok = ::WriteFile( _hFile, lpBuffer, (DWORD)cbData, &cbWritten, NULL );
			if( !fok ) return LastResult();
			if( cbWritten ) _Status |= F_MODIFIED;
			if( lpEff ) *lpEff = cbWritten;
			return S_OK;
		}
		STDMETHOD(Seek)( INT64 liDistance, SEEK_FORM emFrom )
		{
			LARGE_INTEGER tmp;
			tmp.QuadPart = liDistance;
			BOOL fok = ::SetFilePointerEx( _hFile, tmp, &tmp, (DWORD)emFrom );
			if( !fok ) return LastResult();
			return S_OK;
		}
		STDMETHOD(Tell)( UINT64 * puliPointer )
		{
			if( !puliPointer ) return E_INVALIDARG;
			LARGE_INTEGER tmp;
			tmp.QuadPart = 0;
			BOOL fok = ::SetFilePointerEx( _hFile, tmp, &tmp, SEEK_CUR );
			if( !fok ) return LastResult();
			*puliPointer = tmp.QuadPart;
			return S_OK;
		}
		STDMETHOD_(UINT64,Length)()
		{
			LARGE_INTEGER liFileSize;
			BOOL fok = ::GetFileSizeEx( _hFile, &liFileSize );
			if( !fok ) return LENGTH_ERROR;
			return liFileSize.QuadPart;
		}
		STDMETHOD(Readp)( UINT64 uliPos, LPVOID lpBuffer, SIZE_T cbData, SIZE_T * lpEff )
		{
			RFAILED( Seek( uliPos, FROM_BEGIN ) );
			RFAILED( Read( lpBuffer, cbData, lpEff ) );
			return S_OK;
		}
		STDMETHOD(Writep)( UINT64 uliPos, LPCVOID lpBuffer, SIZE_T cbData, SIZE_T * lpEff )
		{
			RFAILED( Seek( uliPos, FROM_BEGIN ) );
			RFAILED( Write( lpBuffer, cbData, lpEff ) );
			return S_OK;
		}
		STDMETHOD(SetPos)( UINT64 uliPos )
		{
			return ( Seek( uliPos, FROM_BEGIN ) );
		}
	};
};


#endif